<template>
  <!-- 搜索表单 -->
  <SearchForm
    v-show="isShowSearch"
    :search="handleSearch"
    :reset="handleReset"
    :change="handleSearch"
    :columns="searchColumns"
    :search-param="searchParam"
    :search-col="searchCol"
  />

  <!-- 工具栏 -->
  <div class="card-toolbar">
    <div class="toolbar-left">
      <slot name="toolbar-left"></slot>
    </div>
    <div class="toolbar-right">
      <slot name="search-buttons"></slot>
      <slot name="toolbar-right"></slot>
      <template v-if="toolButton">
        <el-button v-if="showToolButton('refresh')" :icon="Refresh" circle @click="refresh" />
        <el-button
          v-if="showToolButton('search') && searchColumns?.length"
          :icon="Search"
          circle
          @click="isShowSearch = !isShowSearch"
        />
      </template>
    </div>
  </div>

  <!-- 卡片容器 -->
  <div class="pro-card-container">
    <!-- 卡片列表容器 -->
    <div class="card-list-container">
      <!-- 卡片列表 -->
      <div class="card-list">
        <CardItem v-for="(item, index) in cardData" :key="index" :data="item">
          <!-- 卡片头部插槽 -->
          <template #header="scope">
            <slot name="card-header" v-bind="scope"></slot>
          </template>

          <!-- 卡片内容插槽 -->
          <template #default="scope">
            <slot name="card-content" v-bind="scope"></slot>
          </template>

          <!-- 卡片底部插槽 -->
          <template #footer="scope">
            <slot name="card-footer" v-bind="scope"></slot>
          </template>
        </CardItem>
      </div>
    </div>

    <!-- 分页组件 -->
    <div class="pagination-container">
      <slot name="pagination">
        <Pagination
          v-if="paginationConfig"
          :pageable="pagination"
          :handle-size-change="handleSizeChange"
          :handle-current-change="handleCurrentChange"
        />
      </slot>
    </div>
  </div>
</template>

<script setup lang="ts" name="ProCard">
import { ref, reactive, computed, onMounted, unref, provide } from "vue";
import SearchForm from "@/components/SearchForm/index.vue";
import CardItem from "./components/CardItem.vue";
import Pagination from "@/components/ProTable/components/Pagination.vue";
import { CardProps } from "./interface";
import { ColumnProps } from "./interface";
import { handleProp } from "@/utils";
import { copyProperties } from "@/utils";
import { BreakPoint } from "@/components/Grid/interface";
import { Refresh, Search } from "@element-plus/icons-vue";

// 定义组件props
const props = withDefaults(defineProps<CardProps>(), {
  pagination: true,
  initParam: () => ({}),
  dataCallback: (data: any) => data,
  beforeSearch: (params: any) => params,
  afterSearch: (data: any) => data,
  searchCol: () => ({ xs: 2, sm: 4, md: 4, lg: 5, xl: 6 }) as Record<BreakPoint, number>,
  toolButton: true
});

// 搜索列配置
const searchCol = computed(() => {
  return props.searchCol;
});

// 控制 ToolButton 显示
const showToolButton = (key: "refresh" | "search") => {
  return Array.isArray(props.toolButton) ? props.toolButton.includes(key) : props.toolButton;
};

// 是否显示搜索模块
const isShowSearch = ref(true);

// 定义emit事件
const emit = defineEmits<{
  (e: "search", params: any): void;
  (e: "reset"): void;
}>();

// 接收 columns 并设置为响应式
const tableColumns = reactive<ColumnProps[]>(props.columns);

// 扁平化 columns
const flatColumns = computed(() => flatColumnsFunc(tableColumns));

// 定义 enumMap 存储 enum 值（避免异步请求无法格式化单元格内容 || 无法填充搜索下拉选择）
const enumMap = ref(new Map<string, { [key: string]: any }[]>());
const setEnumMap = async ({ prop, enum: enumValue }: ColumnProps) => {
  if (!enumValue) return;
  // 如果当前 enumMap 存在相同的值 return
  if (enumMap.value.has(prop!) && (typeof enumValue === "function" || enumMap.value.get(prop!) === enumValue)) return;

  // 当前 enum 为静态数据，则直接存储到 enumMap
  if (typeof enumValue !== "function") return enumMap.value.set(prop!, unref(enumValue!));

  // 为了防止接口执行慢，而存储慢，导致重复请求，所以预先存储为[]，接口返回后再二次存储
  enumMap.value.set(prop!, []);

  // 当前 enum 为后台数据需要请求数据，则调用该请求接口，并存储到 enumMap
  try {
    const { data } = await enumValue();
    enumMap.value.set(prop!, data);
  } catch (error) {
    console.error(`获取枚举数据失败[${prop}]:`, error);
  }
};

// 注入 enumMap
provide("enumMap", enumMap);

// 扁平化 columns 的方法
const flatColumnsFunc = (columns: ColumnProps[], flatArr: ColumnProps[] = []) => {
  columns.forEach(async col => {
    if (col._children?.length) flatArr.push(...flatColumnsFunc(col._children));
    flatArr.push(col);

    // column 添加默认 isShow && isFilterEnum 属性值
    col.isShow = col.isShow ?? true;
    col.isFilterEnum = col.isFilterEnum ?? true;

    // 设置 enumMap
    await setEnumMap(col);

    // 设置搜索项的默认值
    if (col.search?.defaultValue !== undefined && col.search?.defaultValue !== null) {
      const key = col.search.key ?? handleProp(col.prop!);
      searchInitParam.value[key] = col.search.defaultValue;
      searchParam.value[key] = col.search.defaultValue;
    }
  });
  return flatArr.filter(item => !item._children?.length);
};

// 过滤需要搜索的配置项 && 排序
const searchColumns = computed(() => {
  return flatColumns.value
    ?.filter(item => item.search?.el || item.search?.render)
    .sort((a, b) => (a.search!.order ?? 0) - (b.search!.order ?? 0));
});

// 分页参数
const pagination = reactive({
  pageNum: 1,
  pageSize: 20,
  total: 0
});

const handleSizeChange = (val: number) => {
  pagination.pageSize = val;
};

const handleCurrentChange = (val: number) => {
  pagination.pageNum = val;
};

const resetPagination = () => {
  pagination.pageNum = 1;
  pagination.pageSize = 20;
};

// 搜索参数
const searchParam = ref<{ [key: string]: any }>({});
const searchInitParam = ref<{ [key: string]: any }>({});
// 总参数(包含分页和查询参数)
const totalParam = ref<{ condition: any; options: any }>({ condition: {}, options: {} });

// 设置搜索参数的默认值
const setSearchInitParam = (columns: ColumnProps[]) => {
  columns.forEach(column => {
    const key = column.search?.key ?? column.prop;
    const defaultValue = column.search?.defaultValue;
    if (defaultValue !== undefined && defaultValue !== null) {
      searchInitParam.value[key!] = defaultValue;
      searchParam.value[key!] = defaultValue;
    }
  });
};

// 重置搜索参数
const resetSearchParam = () => {
  searchParam.value = { ...searchInitParam.value };
};

// 设置搜索表单默认排序 && 搜索表单项的默认值
const setInitSearch = () => {
  // 设置 搜索表单默认排序 && 搜索表单项的默认值
  searchColumns.value?.forEach((column, index) => {
    column.search!.order = column.search?.order ?? index + 2;
    const key = column.search?.key ?? handleProp(column.prop!);
    const defaultValue = column.search?.defaultValue;
    if (defaultValue !== undefined && defaultValue !== null) {
      searchInitParam.value[key] = defaultValue;
      searchParam.value[key] = defaultValue;
    }
  });
};

// 更新查询参数
const updatedTotalParam = () => {
  totalParam.value = { condition: {}, options: {} };
  let likeParam: any[] = [];
  let inParam: any[] = [];
  // 处理查询参数，可以给查询参数加自定义前缀操作
  let nowSearchParam: { [key: string]: any } = {};
  // 防止手动清空输入框携带参数（这里可以自定义查询参数前缀）
  for (let key in searchParam.value) {
    // 某些情况下参数为 false/0 也应该携带参数
    if (searchParam.value[key] || searchParam.value[key] === false || searchParam.value[key] === 0) {
      nowSearchParam[key] = searchParam.value[key];
    }

    // 处理模糊查询，对于输入字段默认支持模糊查询
    props.columns.forEach(column => {
      if (column._children) {
        column._children.forEach(child_column => {
          if (child_column.prop && child_column.prop === key) {
            // 处理模糊查询
            if (child_column.search?.el === "input" && child_column.search.like !== false) {
              likeParam.push({ like: { [key]: nowSearchParam[key] } });
              delete nowSearchParam[key];
            }
            // 处理多选
            else if (
              (child_column.search?.el === "select" ||
                child_column.search?.el === "select-v2" ||
                child_column.search?.el === "tree-select" ||
                child_column.search?.el === "cascader") &&
              child_column.search?.props?.multiple === true &&
              Array.isArray(nowSearchParam[key]) &&
              nowSearchParam[key].length > 0
            ) {
              inParam.push({ [key]: { $in: nowSearchParam[key] } });
              delete nowSearchParam[key];
            }
          }
        });
      } else if (column.prop && column.prop === key) {
        // 处理模糊查询
        if (column.search?.el === "input" && column.search.like !== false) {
          likeParam.push({ like: { [key]: nowSearchParam[key] } });
          delete nowSearchParam[key];
        }
        // 处理多选
        else if (
          (column.search?.el === "select" ||
            column.search?.el === "select-v2" ||
            column.search?.el === "tree-select" ||
            column.search?.el === "cascader") &&
          column.search?.props?.multiple === true &&
          Array.isArray(nowSearchParam[key]) &&
          nowSearchParam[key].length > 0
        ) {
          inParam.push({ [key]: { $in: nowSearchParam[key] } });
          delete nowSearchParam[key];
        }
      }
    });
  }

  // 处理模糊查询参数
  if (likeParam.length > 0) {
    nowSearchParam["$and"] = [...likeParam];
  }

  // 处理多选参数
  if (inParam.length > 0) {
    nowSearchParam["$and"] = nowSearchParam["$and"] ? [...nowSearchParam["$and"], ...inParam] : inParam;
  }

  copyProperties(totalParam.value.condition, nowSearchParam);
};

// 卡片数据
const cardData = ref<any[]>([]);

// 加载状态
const loading = ref(false);

// 分页配置
const paginationConfig = computed(() => {
  return props.pagination ? pagination : null;
});

// 初始化搜索参数
onMounted(() => {
  setInitSearch();
  setSearchInitParam(props.columns);
  getCardData();
});

// 获取卡片数据
const getCardData = async () => {
  try {
    loading.value = true;

    // 更新总参数
    updatedTotalParam();

    // 构建请求参数
    const params: any = {
      ...props.initParam,
      condition: totalParam.value.condition,
      options: {
        pageNum: pagination.pageNum,
        pageSize: pagination.pageSize
      }
    };

    // 搜索前处理参数
    const processedParams = props.beforeSearch(params);

    // 调用API获取数据
    const response = await props.requestApi(processedParams);

    // 处理返回的数据
    const data = props.dataCallback(response.data);

    // 搜索后处理数据
    const processedData = props.afterSearch(data);

    cardData.value = processedData.content || processedData;
    pagination.total = processedData.total || 0;

    emit("search", processedParams);
  } catch (error) {
    console.error("获取卡片数据失败:", error);
  } finally {
    loading.value = false;
  }
};

// 搜索
const handleSearch = () => {
  pagination.pageNum = 1;
  getCardData();
  emit("search", searchParam.value);
};

// 重置
const handleReset = () => {
  resetSearchParam();
  resetPagination();
  getCardData();
  emit("reset");
};

// 刷新
const refresh = () => {
  getCardData();
};

// 暴露给父组件的方法
defineExpose({
  refresh,
  handleSearch,
  handleReset
});
</script>

<style lang="scss" scoped>
.card-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18px;
  margin-bottom: 10px;
  background-color: var(--el-bg-color);
  border: 1px solid var(--el-border-color-light);
  border-radius: 6px;
  .toolbar-left {
    display: flex;
    gap: 10px;
    align-items: center;
  }
  .toolbar-right {
    display: flex;
    gap: 10px;
    align-items: center;
  }
}
.pro-card-container {
  display: flex;
  flex: 1;
  flex-direction: column;
  height: 100%;
  overflow-y: auto;
  .card-list-container {
    flex: 1;
    overflow: hidden;
    overflow-y: auto;
    .card-list {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
      gap: 20px;
    }
  }

  // 分页容器固定在底部
  .pagination-container {
    flex-shrink: 0;
    padding: 0;
  }
}

// 参考 table-search 样式
.table-search {
  padding: 18px 18px 0;
  margin-bottom: 10px;
  .el-form {
    .el-form-item__content > * {
      width: 100%;
    }

    // 去除时间选择器上下 padding
    .el-range-editor.el-input__wrapper {
      padding: 0 10px;
    }
  }
  .operation {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    margin-bottom: 18px;
  }
}
</style>
